home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 May: Tool Chest / Developer CD Series Tool Chest (Apple Computer)(May 1999).iso / Tool Chest / Development Kits / MPW etc / Miscellaneous / MPW p2c / p2cLibraries / PHeap.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-01-29  |  13.0 KB  |  345 lines  |  [TEXT/MPS ]

  1. /*---------------------------------------------------------------------------*
  2.  |                                                                           |
  3.  |                  <<< PHeap.c - Heap Manager Routines >>>                  |
  4.  |                                                                           |
  5.  |                    Copyright Apple Computer, Inc. 1994                      |
  6.  |                            All rights reserved.                           |
  7.  |                                                                           |
  8.  *---------------------------------------------------------------------------*/
  9.  
  10. /* This file contains:
  11.  
  12. initHeap(size, delta, nonCont, proc)    - Heap Manager inititialization
  13. INITHEAP(size, delta, nonCont)                - Pascall callable initHeap
  14. New(amount)                                                        - Allocate amount space on the heap
  15. NEW(amount)                                                        -    Pascal callable version of new
  16. Mark(p)                                                                - Set p to point at current heap position
  17. MARK(p)                                                                - Pascal callable version of mark
  18. Release(p)                                                        - Free heap space back to p
  19.  
  20. */
  21.  
  22. #include <Types.h>
  23. #include <CType.h>
  24. #include <String.h>
  25. #include <Memory.h>
  26. #include <Errors.h>
  27.  
  28. #define DEFAULTSIZE   10000                     /* Default heap size                                        */
  29. #define DEFAULTDELTA  10000                     /* Default heap delta                                        */
  30. #define NONCONTIGUOUS    true                        /* Default for noncontiguous heap chunks*/
  31.  
  32. long initalHeap                = DEFAULTSIZE;    /* Size of initial heap chunk                        */
  33. long heapDelta                 = DEFAULTDELTA;    /* Size of additional heap chunks                */
  34. Boolean nonContiguous    = NONCONTIGUOUS;/* true ==> allow non-contiguous chunks    */
  35. void (*memErrProc)()     = NULL;                    /* Ptr to user's memory error proc            */
  36. Boolean extendFirst        = false;                /* true ==> extend heap before new chunk*/
  37.  
  38. OSErr heapResult            = 0;                        /* Most recent heap error result code        */
  39.  
  40. typedef struct HeapHdr {                            /* Layout of a heap chunk header                */
  41.     struct HeapHdr *nextHeap;                        /*   Ptr to next heap chunk                            */
  42.     long heapSize;                                            /*   Size of this chunk including hdr        */
  43. };
  44.  
  45. #ifndef __cplusplus
  46. typedef struct HeapHdr HeapHdr;
  47. #endif
  48.  
  49. static HeapHdr *heapStart = NULL;            /* Ptr to 1st heap chunk                                */
  50. static HeapHdr *currHeap  = NULL;            /* Ptr to current heap chunk                        */
  51. static char      *heapPtr        = NULL;            /* Ptr to next byte to allocate in heap    */
  52.  
  53.  
  54.  
  55. /*----------------------------------------*
  56.  | initHeap - Heap Manager Initialization |
  57.  *----------------------------------------*
  58.     
  59.  Call this proc to initialize the Heap Manager.  The parameters detemine
  60.  certain characteristics of the way the heap is allocated.
  61.  
  62.  The initSize parameter determines the initial size (in bytes) of the
  63.  unallocated heap.  When this space if fully allocated an attempt is made to
  64.  extend this space contiguously with the original space.  If that extension is
  65.  unsuccessful, and the parameter nonCont is set to true, a new "chunk" of
  66.  (generally, non-contiguous) heap space is allocated.  The size of this
  67.  additional heap space is specified by the delta parameter (again the size is
  68.  in bytes).
  69.  
  70.  If anything goes wrong during allocation of the heap or space within it, for
  71.  example, additional contiguous space cannot be allocated and nonCont was set
  72.  false, then the Heap Manager uses the errProc to report the error to the
  73.  caller.  The errProc call can be done from initHeap, new, or release, and they
  74.  assume the following declaration:
  75.  
  76.  void errProc(heapResult)
  77.      OSErr heapResult;
  78.  {
  79.      - - -
  80.  }
  81.  
  82.  The heapResult is an error number appropriate to the situation usually
  83.  reported by the underlying system memory management routines (the routines the
  84.  Heap Manager itself uses).  The errProc may or may not return to the Heap
  85.  Manager.  If it does, error reporting from the individual Heap Manager
  86.  routines is the same as if no errProc was supplied which is the case we will
  87.  describe shortly.  Either way, the heapResult code of the most recent Heap
  88.  Manager call is available in the global heapResult.
  89.  
  90.  If there is no errProc, i.e., NULL is specified, or, as we just said, the
  91.  errProc returns, then error reporting is a function of each routine and is
  92.  discussed with the description of that routine.  In the case of initHeap,
  93.  an error code (the same one passed to the errProc) is returned as the
  94.  function result, or 0 if there is no error.
  95.  
  96.  The only error that could result from calling initHeap is a failure to
  97.  allocate the initial heap of initSize bytes.  It is assumed initHeap is called
  98.  only once, and at the beginning of execution.  Thus, generally you don't have
  99.  to check the return code because, if the initial allocation fails, something
  100.  is seriously wrong with your system, and something else is also bound to fail
  101.  that you will check!
  102.  
  103.  If initHeap is NOT called, then it will be automatically called on the first
  104.  call to new.  The call will be initHeap(10000, 10000, true, NULL) (and, by the
  105.  way, the return code IS checked by new -- new has its own way of reporting
  106.  errors to its caller).
  107.  
  108.  If initHeap is NOT called, then it will be automatically called on the first
  109.  call to new.  The call will be done as,
  110.  
  111.  initHeap(initalHeap, heapDelta, nonContiguous, memErrProc);
  112.  
  113.  (and, by the way, the return code IS checked by new -- new has its own way of
  114.  reporting errors to its caller).  The parameters are global variables that
  115.  YOU may set.  Thus you may bypass the explicit initHeap, letting new do it,
  116.  with values that you can set.  If you don't set them then they are defaulted
  117.  to give an initHeap call of initHeap(10000, 10000, true, NULL);
  118. */
  119.  
  120. OSErr initHeap(long initSize, long delta, Boolean nonCont, void (*errProc)())
  121. {
  122.     long heapSize = initSize + sizeof(HeapHdr);
  123.  
  124.     heapResult = noErr;
  125.     
  126.     if (heapStart == NULL) {
  127.         heapDelta         = delta + sizeof(HeapHdr);
  128.         nonContiguous = nonCont;
  129.         memErrProc      = errProc;
  130.         extendFirst        = false;
  131.         
  132.         if (currHeap = heapStart = (HeapHdr *)NewPtr(heapSize)) {
  133.             heapStart->heapSize = heapSize;
  134.             heapStart->nextHeap = NULL;
  135.             heapPtr = (char *)heapStart + sizeof(HeapHdr);
  136.             return (noErr);
  137.         }
  138.         
  139.         heapResult = MemError();
  140.         if (memErrProc) (*memErrProc)(heapResult);
  141.         return (heapResult);
  142.     }
  143. }
  144.  
  145. /*------------------------------------------------------------------*
  146.  | INITHEAP - Heap Manager Initialization (Pascal callable version) |
  147.  *------------------------------------------------------------------*
  148.  
  149.  Note: The error return is ignored and the errProc is not supported!
  150. */
  151.  
  152. pascal void INITHEAP(long initSize, long delta, Boolean nonCont)
  153. {
  154.     initHeap(initSize, delta, nonCont, NULL);
  155. }
  156.  
  157. /*---------------------------*
  158.  | New - Allocate Heap Space |
  159.  *---------------------------*
  160.  
  161.  Allocate amount bytes of heap space.  The amount is rounded up to an even
  162.  value if necessary.  If the space cannot be allocated NULL is returned (over
  163.  and above any error reporting done by the errProc specified to initHeap).
  164.  
  165.  The initHeap routine is called on the first call to new if initHeap was not
  166.  explicitly called prior to calling new.
  167. */
  168.  
  169. void *New(long amount)
  170. {
  171.     long newSize, heapSize, amt;
  172.     char *heapPtr0;
  173.     HeapHdr *newHeapChunk;
  174.         
  175.     heapResult = noErr;
  176.  
  177.     /* Init the heap if not already inited. */
  178.     
  179.     if (!heapStart)
  180.         if (initHeap(initalHeap, heapDelta, nonContiguous, memErrProc))
  181.             return (NULL);
  182.     
  183.     /* Try to carve the space out of our existing heap chunk. */
  184.     
  185.     if ((amt = amount) & 1) amt++;                                            /* allocate even amount    */
  186.     
  187.     heapPtr0 = heapPtr;                                                                    /* ptr before updating    */
  188.     newSize = (heapPtr += amt) - (char *)currHeap;            /* ptr after updating        */
  189.     
  190.     if (newSize <= currHeap->heapSize) return (heapPtr0);
  191.     
  192.     /* The new amount won't fit in the current heap chunk.  If the user doesn't */
  193.     /* want non-contiguous allocations we try to extend the current heap chunk  */
  194.     /* by the amount we need. If that fails, it's all over!                                         */
  195.     
  196.     /* For non-contiguous allocations we first try to get another heap chunk and*/
  197.     /* carve the space from that.  If we can't get the new chunk, then we again */
  198.     /* try to extend the current heap chunk by what we need as a last ditch try.*/
  199.     /* Failure here again is bad news!  If, on the other hand, the last ditch     */
  200.     /* try succeeded, then we assume all future trys at getting new heap chunks    */
  201.     /* will fail. So we set a switch, extendFirst, to force us to try to extend    */
  202.     /* the current chunk just as in the contiguous case. The switch is initially*/
  203.     /* false, and only reset to false, once set true, by release() when it has  */
  204.     /* other chenks to free up.                                                                                                    */
  205.     
  206.     /* Note: the original algorithm conceptually always had extendFirst set as    */
  207.     /* true (there was no switch -- but was coded as if the switch was always     */
  208.     /* true).  The effect was to always try to extend the current chunk before    */
  209.     /* going after a new chunk (if non-contiguous -- contiguous is be an error    */
  210.     /* if the extend failed).  In the days of a small heap that was desirable.    */
  211.     /* But with larger present day heaps the effect is to successfully extend        */
  212.     /* the current heap on EVERY New() call (once the initial allocation was         */
  213.     /* used up of course). That's because with a larger application heap, there */
  214.     /* is usually pleanty of room to do the extends. So, to avoid this, we just */
  215.     /* allocate the secondary heap chunks and carve the new space out of them.  */
  216.     /* Only if we can't get a chunk do we fall back on the original algorithm     */
  217.     /* and do the extends, and continue to do them until release() frees a             */
  218.     /* chunk.  Hence the reason for the extendFirst switch.                                             */
  219.     
  220.     if (!nonContiguous || extendFirst) {                                /* contiguous | desprate*/
  221.         SetPtrSize((char *)currHeap, newSize);                        /* extend by SetPtrSize    */
  222.         if ((heapResult = MemError()) == noErr) {                    /*           **********    */
  223.             currHeap->heapSize = newSize;
  224.             return (heapPtr0);
  225.         }
  226.         if (!nonContiguous) {                                                            /* extend failed                */
  227.             if (memErrProc) (*memErrProc)(heapResult);
  228.             return (NULL);
  229.         }
  230.     }
  231.     
  232.     heapSize = amt + sizeof(HeapHdr);                                        /* get new heap chunk        */
  233.     if (heapSize < heapDelta) heapSize = heapDelta;
  234.     
  235.     if (newHeapChunk = (HeapHdr *)NewPtr(heapSize)) {        /* extend by NewPtr            */
  236.         newHeapChunk->heapSize = heapSize;                                /*           ******            */
  237.         newHeapChunk->nextHeap = NULL;
  238.         currHeap->nextHeap = newHeapChunk;                                /* chain chunks together*/
  239.         currHeap = newHeapChunk;
  240.         heapPtr = (heapPtr0 = (char *)newHeapChunk + sizeof(HeapHdr)) + amt;
  241.         return (heapPtr0);
  242.     } else if (!extendFirst) {                                                    /* didn't get heap chunk*/
  243.         SetPtrSize((char *)currHeap, newSize);                        /* extend by SetPtrSize    */
  244.         if ((heapResult = MemError()) == noErr) {                    /*           **********    */
  245.             extendFirst = true;                                                            /* extend 1st next time    */
  246.             currHeap->heapSize = newSize;
  247.             return (heapPtr0);
  248.         }
  249.     }
  250.     
  251.     heapResult = MemError();
  252.     if (memErrProc) (*memErrProc)(heapResult);
  253.     return (NULL);
  254. }
  255.  
  256.  
  257. /*-----------------------------------------------------*
  258.  | NEW - Allocate Heap Space (Pascal callable version) |
  259.  *-----------------------------------------------------*/
  260.  
  261. pascal char *NEW(long amount)
  262. {
  263.     return(New(amount));
  264. }
  265.  
  266.  
  267. /*-------------------------------------------------------------------------*
  268.  | Mark - "Mark" the heap, i.e., remember the current top of heap location |
  269.  *-------------------------------------------------------------------------*
  270.  
  271.  Set a pointer to the heap area.  The current "top" of heap is returned in the
  272.  parameter p and as the function result.  A subsequent call to release will
  273.  free all heap space from this point, p, on.
  274. */
  275.  
  276. void *Mark(void **p)
  277. {
  278.     if (!heapStart)
  279.         if (initHeap(initalHeap, heapDelta, nonContiguous, memErrProc))
  280.             return (NULL);
  281.  
  282.     heapResult = noErr;
  283.     return(*p = heapPtr);
  284. }
  285.  
  286.  
  287. /*--------------------------------------------------*
  288.  | MARK - "Mark" the heap (Pascal callable version) |
  289.  *--------------------------------------------------*/
  290.  
  291. pascal void MARK(void **p)
  292. {
  293.     if (!heapStart)
  294.         if (initHeap(initalHeap, heapDelta, nonContiguous, memErrProc)) {
  295.             *p = NULL;
  296.             return;
  297.         }
  298.  
  299.     heapResult = noErr;
  300.     *p = heapPtr;
  301. }
  302.  
  303.  
  304. /*--------------------------------------------------------*
  305.  | Release - Free heap space back to the specified "mark" |
  306.  *--------------------------------------------------------*
  307.  
  308.  Deallocate all heap space back to the specified "mark", p.  It is assumed p
  309.  is a value set by a mark call.  All space from allocated after that mark call
  310.  is deallocated.  Pointers to that area should NOT be used again!  Errors are
  311.  not explicitly reported from a call to this proc except perhaps through the
  312.  errProc specified to initHeap.
  313. */
  314.  
  315. void Release(char *p)
  316. {
  317.     register HeapHdr *chunkPtr, *nextHeap;
  318.  
  319.     heapResult = noErr;
  320.     
  321.     if (nonContiguous) {
  322.         for (chunkPtr = heapStart; chunkPtr; chunkPtr = chunkPtr->nextHeap)
  323.             if (p >= (char *)chunkPtr + sizeof(HeapHdr) && 
  324.                     p <= (char *)chunkPtr + chunkPtr->heapSize) goto gotIt;
  325.         
  326.         heapResult = memPCErr;
  327.         if (memErrProc) (*memErrProc)(memPCErr);
  328.         return;
  329.     
  330.         gotIt:
  331.         
  332.         currHeap = chunkPtr;
  333.         nextHeap = chunkPtr->nextHeap;
  334.         chunkPtr->nextHeap = NULL;
  335.         for (chunkPtr = nextHeap; chunkPtr; chunkPtr = nextHeap) {
  336.             nextHeap = chunkPtr->nextHeap;
  337.             DisposPtr((char *)chunkPtr);
  338.             extendFirst = false;            /* let New() get chunks 1st again */
  339.         }
  340.     }
  341.     
  342.     heapPtr = p;
  343.     return;
  344. }
  345.